#ifndef SCGI_H
#define SCGI_H

#include "Session.h"
#include "Util.h"

#include <memory>
#include <string>
#include <map>

#include <boost/asio.hpp>

using boost::asio::ip::tcp;

namespace scgi
{

class Server;
class Application;

struct Parser : std::enable_shared_from_this<Parser>
{
	typedef std::shared_ptr<Parser> pointer;

	size_t state, length, current, error;
	std::string key, value;

	Parser();

	int push(char * & first_, char * & last_);

	static Parser::pointer create();
};

struct RequestPath
{
	std::string path, file, query;

	void parse(std::string input_);
};

struct Request : std::enable_shared_from_this<Request>
{
	typedef std::shared_ptr<Request> pointer;

	Server & server;
	tcp::socket socket;

	size_t buffer_size;
	std::shared_ptr<char> buffer;
	char * first, * last;


	RequestPath path;
	size_t content_length;

	std::map<std::string, std::string> env;
	Cookie cookie;
	Session::pointer session;

	Request(Server & server_);

	void start();
	void read(Parser::pointer parser_);
	void start_application();
	void read(std::shared_ptr<Application> application_, size_t remainings_);

	void close_socket();

	void error(unsigned short code_);

	static pointer create(Server & server_);
};

class Application
{
public:
	typedef std::shared_ptr<Application> pointer;

protected:
	Request::pointer request;

public:
	virtual ~Application();

	template <typename T1_, typename T2_>
	void write(std::string & string_, T1_ * this_, T2_ fn_)
	{
		typename T1_::pointer obj = this_->shared_from_this();

		boost::asio::async_write(
			  request->socket
			, boost::asio::buffer(string_)
			, boost::asio::transfer_all()
			, [=](boost::system::error_code const & error_, size_t)
			{
				if(!error_) { (obj.get()->*fn_)(); }
				else { obj->error(500); }
			});
	}
	
	Time now();

	void close_socket();

	Session::pointer create_session(Time expires_);
	Session::pointer get_session();
	void set_session(Session::pointer session_);

	size_t get_cached_session_size() const;

	void assign(Request::pointer request_);
	virtual void start();
	virtual void push(char * first_, char * last_);
	virtual void eos();
	virtual void error(unsigned short code_);
};

class Factory
{
public:
	typedef std::shared_ptr<Factory> pointer;

public:
	virtual ~Factory();

	virtual std::string name() const = 0;
	virtual Application::pointer create_application() = 0;
};





} // scgi

#endif // SCGI_H
